﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sharp.Interprocess.MemoryManager;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using FluentAssertions;
using FizzWare.NBuilder;

namespace Sharp.Interprocess.Tests
{
    [TestClass]
    public class MemoryManagerTests
    {
        private const string TEST_MAP_NAME = "MemoryManager";
        private const int MAX_ALLOCATION_TIME_MILLISECONDS = 250;

        [TestMethod]
        public void BuddyMemoryManager_Initialization()
        {
            using (BuddyMemoryManager memoryManager = new BuddyMemoryManager(TEST_MAP_NAME,1024))
            {                
            }
        }

        [TestMethod]
        public void BuddyMemoryManager_MultipleAllocations_ExecutionTimeLessThanSpecifiedMinimum()
        {
            const int ALLOCATION_COUNT = 50;
            var randomGenerator = new RandomGenerator();
            using (BuddyMemoryManager memoryManager = new BuddyMemoryManager(TEST_MAP_NAME, 1024 * 1024))
            {
                for (int allocationIndex = 0; allocationIndex < ALLOCATION_COUNT; allocationIndex++)
                {
                    var allocationSize = randomGenerator.Next(32, 512);
                    memoryManager.ExecutionTimeOf(manager => manager.Allocate(allocationSize))
                                 .ShouldNotExceed(TimeSpan.FromMilliseconds(MAX_ALLOCATION_TIME_MILLISECONDS));
                }

            }
        }

        [TestMethod]
        public void BuddyMemoryManager_MultipleAllocations_AllocatedSegmentsHaveUniqueOffset()
        {
            const int ALLOCATION_COUNT = 50;
            var randomGenerator = new RandomGenerator();
            using (BuddyMemoryManager memoryManager = new BuddyMemoryManager(TEST_MAP_NAME, 1024 * 1024))
            {
                var allocatedSegmentHandles = new List<SegmentHandle>();
                for (int allocationIndex = 0; allocationIndex < ALLOCATION_COUNT; allocationIndex++)
                {
                    var allocationSize = randomGenerator.Next(32, 512);
                    var allocatedSegment = memoryManager.Allocate(allocationSize);
                    allocatedSegmentHandles.Add(allocatedSegment);
                }

                allocatedSegmentHandles.SelectMany(handle => handle.Segments)
                                       .Select(segment => segment.SegmentOffset)
                                       .Should()
                                       .OnlyHaveUniqueItems();                
            }
        }

        [TestMethod]
        public void BuddyMemoryManager_SingleAllocation_SingleDeallocation_AllocationSegmentHandlesIsEmpty()
        {
            using (BuddyMemoryManager memoryManager = new BuddyMemoryManager(TEST_MAP_NAME, 1024))
            {
                var allocatedSegment = memoryManager.Allocate(512);
                memoryManager.Free(allocatedSegment);
                memoryManager.AllocatedSegmentHandles.Should().BeEmpty();
            }
        }

        [TestMethod]
        public void BuddyMemoryManager_MultipleAllocations_MultipleDeallocations_AllocationSegmentHandlesIsEmpty()
        {
            const int ALLOCATION_COUNT = 50;
            var randomGenerator = new RandomGenerator();
            using (BuddyMemoryManager memoryManager = new BuddyMemoryManager(TEST_MAP_NAME, 1024 * 1024))
            {
                var allocatedSegmentHandles = new List<SegmentHandle>();
                for (int allocationIndex = 0; allocationIndex < ALLOCATION_COUNT; allocationIndex++)
                {
                    var allocationSize = randomGenerator.Next(32, 512);
                    var allocatedSegment = memoryManager.Allocate(allocationSize);
                    allocatedSegmentHandles.Add(allocatedSegment);
                }

                foreach (var allocatedSegmentHandle in allocatedSegmentHandles)
                {
                    memoryManager.Free(allocatedSegmentHandle);
                }
                memoryManager.AllocatedSegmentHandles.Should().BeEmpty();
            }
        }

    }
}
